package wci.frontend.pascal.parsers;

import wci.frontend.*;
import wci.frontend.pascal.*;
import wci.intermediate.*;

import static wci.frontend.pascal.PascalTokenType.*;
import static wci.frontend.pascal.PascalErrorCode.*;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.*;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.*;

/**
 * <h1>StatementParser</h1>
 * <p>解析一个Pascal语句，目前它可能是一个复合语句，有有可能是一个赋值语句</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class StatementParser extends PascalParserTD
{
    public StatementParser(PascalParserTD parent)
    {
        super(parent);
    }

    /**
     * 解析一个语句，以传入的token判断该进行何种解析，子类会覆盖此方法
     * @param token 语句的第一个token
     * @return 分析子树的根节点
     * @throws Exception 
     */
    public ICodeNode parse(Token token)
        throws Exception
    {
        ICodeNode statementNode = null;

        switch ((PascalTokenType) token.getType()) {

        	//复合语句总是以begin开头，如"begin xxxx end." 
            case BEGIN: {
                CompoundStatementParser compoundParser =
                    new CompoundStatementParser(this);
                statementNode = compoundParser.parse(token);
                break;
            }

            //类似于 a = b 之类的赋值语句，这里a就是标识符identifier
            case IDENTIFIER: {
                AssignmentStatementParser assignmentParser =
                    new AssignmentStatementParser(this);
                statementNode = assignmentParser.parse(token);
                break;
            }
            default: {
                statementNode = ICodeFactory.createICodeNode(NO_OP);
                break;
            }
        }

        // 设置根节点“行位置”属性，即第一个token的行位置
        setLineNumber(statementNode, token);

        return statementNode;
    }

    /**
     * 设置节点的行属性。PS: 我认为这种写法很怪，为何不setLineNumber(ICodeNode, int)?
     * @param node 分析树节点
     * @param token token
     */
    protected void setLineNumber(ICodeNode node, Token token)
    {
        if (node != null) {
            node.setAttribute(LINE, token.getLineNumber());
        }
    }

    /**
     * 解析语句列表，比如 a=b; b=c; c=d; 
     * @param token 当前token
     * @param parentNode 父节点即包含此语句列表的节点
     * @param terminator 结束此列表的token类型，一般为END
     * @param errorCode 错误码
     * @throws Exception
     */
    protected void parseList(Token token, ICodeNode parentNode,
                             PascalTokenType terminator,
                             PascalErrorCode errorCode)
        throws Exception
    {
        //遍历每条语句直到遇见结束TOKEN类型或文件结束
        while (!(token instanceof EofToken) &&
               (token.getType() != terminator)) {

            // 解析一条语句
            ICodeNode statementNode = parse(token);
            //语句子树根节点作为子节点附加到父节点上
            parentNode.addChild(statementNode);

            token = currentToken();
            TokenType tokenType = token.getType();

            //每条语句之后肯定是一个分号 ; 否则报错
            if (tokenType == SEMICOLON) {
                token = nextToken();  
            }else if (tokenType == IDENTIFIER) { //遗漏分号
                errorHandler.flag(token, MISSING_SEMICOLON, this);
            }else if (tokenType != terminator) {//除非碰到结束token了
                errorHandler.flag(token, UNEXPECTED_TOKEN, this);
                token = nextToken(); 
            }
        }

        //判断是否已到结束token，如果是就跳过它，否则报错
        if (token.getType() == terminator) {
            token = nextToken();  // consume the terminator token
        }else {
            errorHandler.flag(token, errorCode, this);
        }
    }
}
